/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <mc/log.h>

#include "hi182x_port.h"

namespace dev {

bool hi182x_port::init_optical_module() {
    auto objects = get_children();
    if (objects.empty()) {
        elog("Error: No optical modules found");
        return false;
    }
    for (auto& object : objects) {
        auto object_name = object->get_name();
        if (object_name.substr(0, object_name.find_first_of("_")) == "OpticalTransceiver") {
            ilog("hi182x_port init optical module ${name}", ("name", object_name));
            m_optical_module = static_cast<hi182x_optical_module*>(object.get());
            return true;
        }
    }
    return false;
}

bool hi182x_port::start() {
    if (!init_optical_module()) {
        return false;
    }
    m_optical_module->m_optical_module.set_port_id(m_network_port.PortId);
    m_optical_module->start();
    return true;
}

// 实现network_port的虚函数
bool hi182x_port::stop() {
    return true;
}

// 实现network_port的虚函数
bool hi182x_port::init(mc::mutable_dict& config, const mc::dict& connector) {
    try {
        from_variant(config, *this);

        // 初始化IMU对象
        m_imu = std::make_unique<imu>(get_service());

    } catch (const std::exception& e) {
        elog("hi182x_port init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
    return true;
}
bool hi182x_port::update_port_bdf(imu_pcie_device_req_t& pcie_device_info) {
    pcie_device_info.bus += m_network_port.PortId;

    if (!verify_did_vid_svid_sdid(pcie_device_info)) {
        elog("Error: Failed to verify DID/VID/SVID/SDID");
        return false;
    }

    std::string bdf = std::to_string(pcie_device_info.bus) + ":" +
                      std::to_string(pcie_device_info.device) + "." +
                      std::to_string(pcie_device_info.function);
    m_network_port.BDF = bdf;
    return true;
}

bool hi182x_port::verify_did_vid_svid_sdid(imu_pcie_device_req_t& pcie_device_info) {
    // 获取VID/DID信息
    pcie_device_info.pci_info_address = VID_DID_ADDRESS;
    auto vid_did_data                 = m_imu->get_info_from_imu(true, pcie_device_info);
    if (!vid_did_data || vid_did_data->size() < 4) {
        elog("Error: Failed to get VID/DID info from IMU");
        return false;
    }

    // 手动解析VID/DID值
    uint16_t device_id = ((*vid_did_data)[3] & 0xff) | ((*vid_did_data)[2] & 0xff) << 8;
    uint16_t vendor_id = ((*vid_did_data)[1] & 0xff) | ((*vid_did_data)[0] & 0xff) << 8;

    // 验证VID/DID有效性
    if (vendor_id == 0xffff && device_id == 0xffff) {
        elog("Error: Invalid VID/DID");
        return false;
    }

    // 获取SVID/SDID信息
    pcie_device_info.pci_info_address = SVID_SDID_ADDRESS;
    auto svid_sdid_data               = m_imu->get_info_from_imu(false, pcie_device_info);
    if (!svid_sdid_data || svid_sdid_data->size() < 4) {
        elog("Error: Failed to get info from IMU");
        return false;
    }

    // 手动解析SVID/SDID值
    uint16_t sub_device_id = ((*svid_sdid_data)[3] & 0xff) | ((*svid_sdid_data)[2] & 0xff) << 8;
    uint16_t sub_vendor_id = ((*svid_sdid_data)[1] & 0xff) | ((*svid_sdid_data)[0] & 0xff) << 8;

    // 验证SVID/SDID有效性
    if (sub_vendor_id == 0xffff && sub_device_id == 0xffff) {
        elog("Error: Invalid SVID/SDID");
        return false;
    }

    dlog("Successfully verified DID/VID/SVID/SDID from IMU: VID=0x${vid:04x}, DID=0x${did:04x}, "
         "SVID=0x${svid:04x}, SDID=0x${sdid:04x}",
         ("vid", vendor_id)("did", device_id)("svid", sub_vendor_id)("sdid", sub_device_id));
    return true;
}

bool hi182x_port::start_ncsi_protocol(ncsi_over_mctp_hw_ptr ncsi_over_mctp,
                                      mc::milliseconds      interval) {
    ncsi_over_mctp->init_ncsi_channel(DEFAULT_PACKAGE_ID, m_network_port.PortId);
    
    if (m_optical_module) {
        m_optical_module->start_ncsi_protocol(ncsi_over_mctp, interval);
    }
    
    m_network_port.start_ncsi_update_task_huawei(ncsi_over_mctp, interval);
    m_network_port_link_info.start_ncsi_update_task_huawei(ncsi_over_mctp, interval,
                                                           m_network_port.PortId);
    m_network_port_data_center_bridging.start_ncsi_update_task_huawei(ncsi_over_mctp, mc::milliseconds(60000),
                                                                      m_network_port.PortId);
    m_network_port_metrics.start_ncsi_update_task_huawei(ncsi_over_mctp, mc::milliseconds(60000),
                                                         m_network_port.PortId);
    
    return true;
}

void hi182x_port::stop_ncsi_protocol() {
    // 停止光模块的NCSI任务
    if (m_optical_module) {
        m_optical_module->stop_ncsi_protocol();
    }
    
    // 停止所有网络端口组件的NCSI任务
    m_network_port.stop_ncsi_update_task();
    m_network_port_metrics.stop_ncsi_update_task();
    m_network_port_link_info.stop_ncsi_update_task();
    m_network_port_data_center_bridging.stop_ncsi_update_task();
}

bool hi182x_port::start_smbus_update_task(smbus_obj_ptr smbus_obj, mc::milliseconds interval) {
    if (m_optical_module) {
        m_optical_module->start_smbus_update_task(smbus_obj, interval);
    }
    m_network_port.start_smbus_update_task(smbus_obj, interval, m_network_port.PortId);
    m_network_port_link_info.start_smbus_update_task(smbus_obj, interval, m_network_port.PortId);
    return true;
}

} // namespace dev

MC_REFLECT(dev::hi182x_port,
           ((m_network_port, "bmc.dev.NetworkPort"))((m_network_port_link_info,
                                                      "bmc.dev.NetworkPort.LinkInfo"))(
               (m_network_port_data_center_bridging, "bmc.dev.NetWorkPort.DataCenterBridging"))(
               (m_network_port_lldp_receive, "bmc.dev.NetworkPort.LLDPReceive"))(
               (m_network_port_metrics, "bmc.dev.NetworkPort.Metrics")))
